﻿using HtmlAgilityPack;
using MusicExplorer.Model;
using MusicExplorer.Repository.Interfaces;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fizzler.Systems.HtmlAgilityPack;
using System.Threading.Tasks;
using MusicExplorer.Repository.Internals;
using System.Net;

namespace MusicExplorer.Repository
{
    [Id("C0C5501F-D815-41AF-A3CB-A8163D5D8E5A")]
    public sealed class BeatportRepository : RepositoryBase, ITracksByGenre
    {
        private const string m_websiteUrl = @"http://www.beatport.com";
        private const string m_imageUrl = @"http://geo-media.beatport.com/image_size/{0}x{0}/{1}.jpg";

        private const string m_url_top_100_all =
            @"http://www.beatport.com/top-100";

        public BeatportRepository(Model.Repository repository)
            : base(repository)
        {
            m_urlAllEntities[EntityType.Genre] = m_websiteUrl;
            m_urlTracks[new QueryDefinition(QueryType.AllTracks, EntityType.Genre)] =
                m_websiteUrl + @"/genre/{0}/tracks?perPage=20&sortBy=publishDate+DESC%2C+releaseId+DESC&page={1}";
            m_urlTracks[new QueryDefinition(QueryType.TopTracks, EntityType.Genre)] =
                m_websiteUrl + @"/genre/{0}/top-100";

            m_urlSearchEntities[EntityType.Artist] =
                m_websiteUrl + @"/search?query={0}&facets[0]=fieldType%3Aperformer&perPage=20&page={1}";
            m_urlTracks[new QueryDefinition(QueryType.AllTracks, EntityType.Artist)] =
                m_websiteUrl + @"/artist/{0}/tracks?perPage=20&sortBy=publishDate+DESC%2C+releaseId+DESC&page={1}";

        }

        public override int StartingPage { get { return 1; } }

        public override Dictionary<QueryDefinition, IEnumerable<string>> UnsupportedEntities
        {
            get
            {
                return new Dictionary<QueryDefinition, IEnumerable<string>>()
                {
                    { new QueryDefinition(QueryType.AllTracks, EntityType.Genre), new string[] { "all" } }
                };
            }
        }

        public override bool IsMusicStore { get { return true; } }

        protected override string GetTracksUrl(QueryType queryType, Entity entity, int page)
        {
            if (queryType == QueryType.TopTracks)
            {
                if (page == StartingPage)
                {
                    if (entity.Type == EntityType.Genre && entity.Id == "all")
                    {
                        return m_url_top_100_all;
                    }
                    else
                    {
                        return base.GetTracksUrl(queryType, entity, page);
                    }
                }
                else
                {
                    return string.Empty;
                }
            }
            else
            {
                return base.GetTracksUrl(queryType, entity, page);
            }
        }

        protected override List<Track> DecodeTracks(QueryType type, Entity entity, string source)
        {
            var document = new HtmlDocument();
            document.LoadHtml(source);
            var page = document.DocumentNode;

            var tracks = new List<Track>();

            var trackNodes = page.QuerySelectorAll("[name^='tracks-grid-browse_track_']");
            foreach(var trackNode in trackNodes)
            {
                var span = trackNode.QuerySelector("span");
                var json = span.Attributes["data-json"].Value;
                var json_track = JObject.Parse(json);
                var urlImage = @"http:" + ((string)json_track["dynamicImages"]["main"]["url"]).Replace("{hq}", "");
                var id = (string)json_track["id"];
                var url = trackNode.QuerySelector(".secondColumn").ChildNodes.First(c => c.Name == "a").Attributes["href"].Value;
                var track = new Track(id, m_repository, TrackType.Classic)
                {
                    Name = WebUtility.HtmlDecode((string)json_track["title"]),
                    Mp3 = (string)json_track["sampleUrl"],
                    Artwork = urlImage.Replace("{w}", "296").Replace("{h}", "296"),
                    PublishDate = DateTime.ParseExact((string)json_track["publishDate"], "yyyy-MM-dd", null),
                    Url = url
                };

                var position = trackNode.QuerySelector(".positionColumn");
                if (position != null)
                {
                    track.Order = int.Parse(position.InnerText);
                }

                foreach (var json_artist in (JArray)json_track["artists"])
                {
                    var artistId = (string)json_artist["id"];
                    var artistName = WebUtility.HtmlDecode((string)json_artist["name"]);

                    // Artworks are not use in this case
                    var artist = new Artist(artistId, artistName, m_repository, null, null);

                    var role = (string)json_artist["type"];
                    switch(role)
                    {
                        case "artist":
                            if (!track.Artists.ContainsKey(ArtistRole.Composer)) track.Artists[ArtistRole.Composer] = new List<Artist>();
                            track.Artists[ArtistRole.Composer].Add(artist);
                            break;
                        case "remixer":
                            if (!track.Artists.ContainsKey(ArtistRole.Remixer)) track.Artists[ArtistRole.Remixer] = new List<Artist>();
                            track.Artists[ArtistRole.Remixer].Add(artist);
                            break;
                        default:
                            throw new Exception("Not supported artist type");
                    }
                }
                tracks.Add(track);
            }

            return tracks;
        }

        protected override List<Genre> DecodeGenres(string source)
        {
            var document = new HtmlDocument();
            document.LoadHtml(source);
            var page = document.DocumentNode;

            var genres = new List<Genre>();

            var allGenre = new Genre("all", "All genres", m_repository);
            genres.Add(allGenre);

            var headerNav = page.QuerySelector(".headerNav");
            var menuNode = headerNav.QuerySelector("ul");
            var allGenresNodes = menuNode.QuerySelectorAll("a");
            foreach(var genreNode in allGenresNodes)
            {
                var splittedValues = genreNode.Attributes["href"].Value.Split('/');
                var length = splittedValues.Length;
                var id = splittedValues[length - 2] + '/' + splittedValues[length - 1];
                var name = WebUtility.HtmlDecode(genreNode.InnerText);
                var genre = new Genre(id, name, m_repository);
                genres.Add(genre);
            }

            return genres;
        }

        protected override List<Label> DecodeLabels(string source)
        {
            throw new NotImplementedException();
        }

        protected override List<Artist> DecodeArtists(string source)
        {
            var document = new HtmlDocument();
            document.LoadHtml(source);
            var page = document.DocumentNode;

            var artists = new List<Artist>();

            var listNode = page.QuerySelector(".tile-list-items");
            if (listNode != null)
            {
                foreach (var artistNode in listNode.ChildNodes.Where(n => n.Name == "li"))
                {
                    var linkNode = artistNode.QuerySelector("[name='unit-title']");
                    var url = linkNode.Attributes["href"].Value;
                    var imgNode = artistNode.QuerySelector("img");
                    var imageLink = imgNode.Attributes["src"].Value;
                    var imageId = imageLink.Split('/').Last().Split('.')[0];

                    var splitUrl = url.Split('/');
                    var id = string.Empty;
                    for(int i = 0; i < splitUrl.Length; i++)
                    {
                        if(i == splitUrl.Length - 2)
                        {
                            id = splitUrl[i] + '/' + splitUrl[i+1];
                            break;
                        }
                    }

                    var name = WebUtility.HtmlDecode(linkNode.Attributes["title"].Value);
                    var smallArtwork = string.Format(m_imageUrl, "80", imageId);
                    var bigArtwork = string.Format(m_imageUrl, "296", imageId);
                    var artist = new Artist(id, name, m_repository, smallArtwork, bigArtwork);
                    artists.Add(artist);
                }
            }

            return artists;
        }
    }
}
